Conversation
|
b45d619 to
75cc4ef
Compare
🔗 Cross-repo PR dependenciesThe read-only Build run reported the following dependent PR(s) and fetched head SHA(s): CI run: https://github.com/apache/nuttx/actions/runs/34770810263 |
75cc4ef to
9f8e382
Compare
❌ Cross-repo dependency could not be appliedThe Build report says the declared dependency PR(s) could not be applied, so CI did not run against the combined code: Reason: cherry-pick failed (if your PR has merge commits, rebase instead) CI run: https://github.com/apache/nuttx/actions/runs/34837949470 |
9f8e382 to
98eb16e
Compare
| * forwarder. The new task inherits the creator's D-Space. | ||
| */ | ||
|
|
||
| entry = (main_t)fdpic_callback((FAR void *)entry); |
There was a problem hiding this comment.
but why not change the invocation point by fdpic_invoke instead?
There was a problem hiding this comment.
the new task already gets r9 from the inherited dspace (nxtask_dup_dspace() -> up_initial_state() -> REG_PIC) and keeps it for its life, so only the entry needs unwrapping; fdpic_invoke() is for a callback on a shared worker, which has no base.
6f09b03 to
3426186
Compare
| # define fdpic_invoke(arg, entry, got) \ | ||
| ((void)(got), (((CODE void (*)(uintptr_t))(uintptr_t)(entry))(arg))) | ||
| # define fdpic_invoke(arg, desc) \ | ||
| (((CODE void (*)(uintptr_t))(uintptr_t)(desc)->entry)(arg)) |
There was a problem hiding this comment.
should we cast desc to function pointer directly for no fdpic case?
There was a problem hiding this comment.
I guess we can do it, for no benefit.
| * captured at registration and installed | ||
| * around the call on the worker thread. */ | ||
| #else | ||
| sigev_notify_function_t func; /* Notification function */ |
There was a problem hiding this comment.
why need define desc at line 74? I suppose that func should point to fdpic_desc_s in fdpic case.
There was a problem hiding this comment.
the firmware and the built-in applications are not FDPIC, so their sigev_notify_function is plain. Only way to tell them apart is the registering context.
| * the current directory entry, do so. | ||
| */ | ||
|
|
||
| if (filter && !filter(d)) |
There was a problem hiding this comment.
why not change to fdpic_invoke(d, filter) here? and revert the change at line 103
There was a problem hiding this comment.
fdpic_invoke() returns void, and scandir() needs the return code
|
|
||
| DEBUGASSERT(func != SIG_ERR && func != SIG_HOLD); | ||
|
|
||
| /* Not resolved here. nxsig_action() resolves the handler, which covers |
There was a problem hiding this comment.
remove the comment?
|
|
||
| if (work->desc.got != 0) | ||
| { | ||
| fdpic_invoke((uintptr_t)work->value.sival_ptr, &work->desc); |
There was a problem hiding this comment.
why not call fdpic_invoke(work->value, xxx->sigev_notify_function) directly? and remove the change in mq_notify.c and desc field in sigwork_s.
There was a problem hiding this comment.
I don't get it. nxsig_notification() does not run in the registering context. Drivers, timer_settime() and mq_sndinternal() may not run in the module context.
3426186 to
00bb56e
Compare
The base firmware and an FDPIC module disagree about what a function pointer is. Firmware is not built FDPIC, so to it a pointer is a code address and it branches there. A module passes the address of a two word descriptor instead, because its code and data are placed independently and a bare code address would leave the callee unable to find its own data. A firmware routine that takes a callback therefore branches into the module's data segment and faults. So the ten entry points that can be handed a callback by a module resolve the descriptor before storing or branching to it: qsort, bsearch, pthread_create, signal, sigaction, task_create and task_create_with_stack, task_spawn, pthread_once, scandir, and mq_notify and timer_create with SIGEV_THREAD. Which one resolves matters as much as that one does. Resolving twice would take an already resolved code address for a descriptor and read two words from the instruction stream, so each pointer is resolved exactly once, at the outermost point that sees it. signal() passes its argument through untouched because sigaction() and then nxsig_action() will resolve it, which covers a module calling sigaction() directly as well. qsort() is split so that the public entry resolves and the recursive implementation does not. scandir() resolves its filter but not its comparison function, which it hands to qsort(). Whether a caller is a module at all is asked of the PIC base register, which up_initial_state() sets only for a task that has a D-Space. A plain kernel task therefore reads zero and is left alone. SIGEV_THREAD is the case the register cannot answer, because the callback runs later on a work queue worker that carries no module's base at all. The base is captured instead when the notification is registered, in the module's own context, and installed around the call. All of it is behind CONFIG_FDPIC, which defaults off. Built for mps3-an547:picostest both ways; with it off the entry points compile to what they were. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
nxstyle wants a blank line between a declaration and the statements that follow it. The line is not new, but it sits within three lines of the FDPIC change in this series, so CI reads it as part of the patch. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
00bb56e to
98f9b2a
Compare
Summary
[5/10]#19942,[5.5/10]#19940 and[6/10]#20089 are merged: an FDPIC object is placed, bound and relocated. This makes the firmware callable from one.The base firmware and an FDPIC module disagree about what a function pointer is. Firmware is not built FDPIC, so to it a pointer is a code address and it branches there. A module passes the address of a two word descriptor instead, because its code and data are placed independently and a bare code address would leave the callee unable to find its own data. A firmware routine handed a module's callback therefore branches into the module's data segment and faults.
So the ten entry points that can be handed a callback resolve the descriptor before storing or branching to it:
qsort,bsearch,pthread_create,signal,sigaction,task_createandtask_create_with_stack,task_spawn,pthread_once,scandir, andmq_notifyandtimer_createwithSIGEV_THREAD.Which one resolves matters as much as that one does. Resolving twice would take an already resolved code address for a descriptor and read two words from the instruction stream, so each pointer is resolved exactly once, at the outermost point that sees it.
signal()passes its argument through untouched becausesigaction()and thennxsig_action()will resolve it, which also covers a module callingsigaction()directly.qsort()is split so the public entry resolves and the recursive implementation does not.scandir()resolves its filter but not its comparison function, which it hands toqsort().Whether the caller is a module at all is asked of the PIC base register, which
up_initial_state()sets only for a task that has a D-Space, so a plain kernel task reads zero and is left alone.SIGEV_THREADis the case the register cannot answer, because the callback runs later on a work queue worker carrying no module's base. The base is captured when the notification is registered, in the module's own context, and installed around the call.Impact
All of it is behind
CONFIG_FDPIC, which defaults off and is only selectable whereARCH_HAVE_ELF_FDPICis set.qsort()gains an internal split andscandir()a blank line; everything else is#ifdef'd. With the option off the entry points are what they were.Testing
mps3-an547:picostestbuilds withCONFIG_FDPICoff and on.tools/checkpatch.sh -c -u -m -gpasses.The run time evidence for the descriptors themselves is in
[10/10], whosepimoroni-pico-2-plus:xipfs-fdpiccarriesapps/testing/fs/xipfs: it exercisesqsortandSIGEV_THREADcallbacks from a loaded module, which is what this patch exists to make work.